using System;
using System.Linq;
using System.IO;
using Microsoft.VisualBasic.Devices;
using System.Collections.Generic;

namespace sorting
{
    class Program
    {
        static StreamWriter sw;

        public static void Main(string[] args)
        {
            try
            {
                DateTime dt = DateTime.Now;
                if (File.Exists(args[1]))
                    File.Delete(args[1]);
                var fsinput = new FileStream(args[0], FileMode.Open);
                var fsoutput = new FileStream(args[1], FileMode.OpenOrCreate);
                var sr = new StreamReader(fsinput);
                sw = new StreamWriter(fsoutput);

                string remains = ;
                if (Directory.Exists(tmp))
                    Directory.Delete(tmp, true);
                Directory.CreateDirectory(tmp);

                int bufferSize = 30000000;
                int alreadySorted = 0;

                while (!sr.EndOfStream)
                {
                    bufferSize = GetSizeOfAvailableMemory()  10;
                    var buffer = new char[bufferSize];
                    
                    int amount = sr.ReadBlock(buffer, 0, bufferSize);
                    string input = remains;
                    if ((char)sr.Peek() == ' '  sr.Peek() == -1)
                    {
                        input = input + new string(buffer, 0, amount);
                        remains = ;
                    }
                    else
                    {
                        var bufferStr = new string(buffer, 0, amount);
                        input = input + bufferStr.Substring(0, bufferStr.LastIndexOf(' '));
                        remains = bufferStr.Substring(bufferStr.LastIndexOf(' ') + 1);
                    }

                    if (input.IndexOfAny(new[] { ',', '.', ';', '=', '', '-' }) = 0)
                        throw new FormatException();
                    string[] numbers = input.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                    var a = new uint[numbers.Count()];array to sort

                    for (int i = 0; i  a.Count(); i++)
                        a[i] = Convert.ToUInt32(numbers[i]); infill of array

                    Qsort(a, 0, a.Count() - 1);sort

                    var fileName = Path.Combine(tmp,tmp + alreadySorted.ToString());
                    if (File.Exists(fileName))
                        File.Delete(fileName);
                    var tmpFS = new FileStream(fileName, FileMode.Create);
                    var tmpSW = new StreamWriter(tmpFS);
                    foreach (uint num in a)
                        tmpSW.WriteLine(num);
                    tmpSW.Close();
                    tmpFS.Close();
                    alreadySorted++;
                    Console.WriteLine(Already sorted {0} part with size {1}b, alreadySorted, bufferSize);
                }

                MergeFiles(alreadySorted);
                sw.Close();
                DateTime dt1 = DateTime.Now;
                TimeSpan period = dt1 - dt;
                Directory.Delete(tmp, true);
                Console.WriteLine({0}{1}{2}.{3}, period.Hours, period.Minutes, period.Seconds, period.Milliseconds);
                Console.ReadKey();
            }
            catch (OverflowException)
            {
                Console.WriteLine(too big number for int32);
                Console.ReadKey();
            }
            catch (FileNotFoundException)
            {
                Console.WriteLine(input file wasn't found);
                Console.ReadKey();
            }
            catch (FormatException)
            {
                Console.WriteLine(invalid data format);
                Console.ReadKey();
            }
            catch (OutOfMemoryException)
            {
                Console.WriteLine(too big input);
                Console.ReadKey();
            }
        }

        private static void MergeFiles(int numFiles)
        {
            bool isAnyNonSavedNum = true;
            var srList = new ListStreamReader();
            var isEndedList = new Listbool();

            for (int i = 0; i  numFiles; i++)
            {
                srList.Add(new StreamReader(Path.Combine(tmp, tmp + i.ToString())));
                isEndedList.Add(false);
            }

            uint alreadyWritten = 0;
            var a = new uint[numFiles];
            int index = 0;

            for (int i = 1; i  numFiles; i++)
            {
                a[i] = Convert.ToUInt32(srList[i].ReadLine());
                if (srList[i].EndOfStream)
                    Console.WriteLine({0} file is close to be merged, i);
            }
            while (isAnyNonSavedNum)
            {
                if (!isEndedList[index])
                {
                    a[index] = Convert.ToUInt32(srList[index].ReadLine());
                    if (srList[index].EndOfStream)
                        Console.WriteLine({0} file is close to be merged(1 el left), index);
                }
                uint min = FindMinElement(a, out index);
                sw.Write(min +  );
                alreadyWritten++;
                if (srList[index].EndOfStream)
                {
                    isEndedList[index] = true;
                    a[index] = UInt32.MaxValue;
                }
                isAnyNonSavedNum = Check(isEndedList);
            }

            foreach (StreamReader sr in srList)
                sr.Close();
        }

        private static bool Check(Listbool isEndedList)
        {
            foreach (bool f in isEndedList)
                if (f == false)
                    return true;
            return false;
        }   

        private static uint FindMinElement(uint[] a, out int minIndex)
        {
            uint min = a[0];
            minIndex = 0;
            for (int i = 0; i  a.Count(); i++)
                if (a[i]  min)
                {
                    min = a[i];
                    minIndex = i;
                }
            return min;
        }

        static int GetSizeOfAvailableMemory()
        {
            return (int)((new ComputerInfo()).AvailablePhysicalMemory  0.8);    multiple 0.8 to left some space for
        }                                                                       the system

        static void Qsort(uint[] arr, int arrBegin, int arrEnd) 
        {
	        int indexBegin	= arrBegin;	left index from which we start looking from
	        int indexEnd	= arrEnd;	right index from which we start looking from

	        uint middleElement = arr[(arrEnd + arrBegin)  2];middle element of array
	
	        while (indexBegin = indexEnd) 
	        {
		        while (arr[indexBegin]  middleElement)
			        indexBegin++;looking through left part
		
		        while (arr[indexEnd]  middleElement)
			        indexEnd--;looking through right part
		
		        if (indexBegin = indexEnd) 
		        {
                    if (indexBegin  indexEnd)
                    {
                        uint t = arr[indexBegin];
                        arr[indexBegin] = arr[indexEnd];
                        arr[indexEnd] = t;
                    }
		            indexBegin++;going to other elements
			        indexEnd--;
		        }
	
	        }

            if (arrBegin  indexEnd)
		        Qsort(arr, arrBegin, indexEnd);started sort for part of array
	        if (indexBegin  arrEnd)
		        Qsort(arr, indexBegin, arrEnd);started sort for part of array
        }
    }
}